﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Xml.Schema;
using log4net;
using SmallBusinessKit.DataAccess.Interfaces;
using SmallBusinessKit.Models.Entities;
using SmallBusinessKit.Models.NonEntities;

namespace SmallBusinessKit.DataAccess.Infrastructure
{
    public class XmlDataParser : IXmlDataParser
    {
        /// <summary>
        /// This class is used to populate the XmlRepositories during configuration
        /// but also doubles up to seed SqlSvr if database is used...
        /// </summary>
        
        #region Properties

        private readonly ILog    _log = LogManager.GetLogger(typeof(XmlDataParser));

        private readonly string _categoryXmlFileName;
        private readonly string _itemsXmlFileName;
        private readonly string _newsXmlFileName;
        private readonly string _peopleXmlFileName;
        private readonly string _testimonialXmlFileName;

        #endregion

        #region Methods

        public XmlDataParser(string categoryXmlName, string itemsXmlName, string peopleXmlName, string newsXmlName, string testimonialXmlName)
        {
            var baseDir = AppDomain.CurrentDomain.GetData("DataDirectory");

#if DEBUG
            if (UnitTestDetector.IsInUnitTest)
            {
                baseDir = ".\\App_Data";
            }
#endif
            _categoryXmlFileName    = $"{baseDir}\\{categoryXmlName}";
            _itemsXmlFileName       = $"{baseDir}\\{itemsXmlName}";
            _peopleXmlFileName      = $"{baseDir}\\{peopleXmlName}";
            _newsXmlFileName        = $"{baseDir}\\{newsXmlName}";
            _testimonialXmlFileName = $"{baseDir}\\{testimonialXmlName}";
        }

        Func<XElement, string> HasValue = x =>
        {
            string r = null;

            if (!String.IsNullOrEmpty(x.Value))
            {
                if (!x.Value.Equals("NULL"))
                {
                    r = x.Value;
                }
            }
            return (r);
        };

        public IList<Category> ParseCategories()
        {
            var categories = new List<Category>();
            var items      = new List<Item>();

            var xmlDoc = XDocument.Load(_categoryXmlFileName);

            var xsd = _categoryXmlFileName.Replace(".xml", ".xsd").Replace("App_Data", "App_Data\\schemas"); // quick hack. Fix this later
            if (File.Exists(xsd))
            {
                var schemaSet = new XmlSchemaSet();
                schemaSet.Add("", xsd);
                schemaSet.Compile();
                xmlDoc.Validate(schemaSet, (o, ex) => { _log.Error(ex.Message); });
            }

            var xeCats  = xmlDoc.Descendants("category").ToList();
            var xeItems = xmlDoc.Descendants("item").ToList();

            xeCats.ForEach(xe => categories.Add(
                                                new Category
                                                {
                                                    Id              = xe.Element("id").Value,
                                                    ParentId        = HasValue(xe.Element("parentCategoryId")),
                                                    Title           = xe.Element("title").Value,
                                                    Desc            = xe.Element("description").Value,
                                                    ImageUrl        = xe.Element("imageUrl").Value,
                                                    ImageAltText    = xe.Element("imageAltText").Value,
                                                    IsVisible       = Convert.ToBoolean(xe.Element("visible").Value),
                                                }));

            xeItems.ForEach(xe => items.Add(
                                                new Item
                                                {
                                                    Id              = xe.Element("id").Value,
                                                    Title           = xe.Element("title").Value,
                                                    Desc            = xe.Element("description").Value,
                                                    Price           = Convert.ToDecimal(xe.Element("price").Value),
                                                    IsInStock       = Convert.ToBoolean(xe.Element("inStock").Value),
                                                    IsVisible       = Convert.ToBoolean(xe.Element("visible").Value),
                                                    ImageUrl        = xe.Element("imageUrl").Value,
                                                    ImageAltText    = xe.Element("imageAltText").Value
                                                }));

            categories.ForEach(category =>
            {
                category.ChildCategories = categories.Where(subCategory => subCategory.ParentId == category.Id)
                                                   .Select(sc => sc)
                                                   .ToList();

                category.Parent = (from pc in categories.Where(i => i.Id == category.ParentId)
                            select new Category
                            {
                                Id           = pc.Id,
                                Title        = pc.Title,
                                Desc         = pc.Desc,
                                IsVisible    = pc.IsVisible,
                                ImageUrl     = pc.ImageUrl,
                                ImageAltText = pc.ImageAltText,

                            }).FirstOrDefault();
            });

            categories.ForEach(c =>
            {
                var xc = xeCats.Where(xe => xe.Element("id").Value == c.Id)
                                .Select(xe => xe)
                                .Descendants("childItemId")
                                .Select(xe => xe.Value)
                                .ToList();
                c.Items = items.Where(i => xc.Contains(i.Id)).Select(i => i).ToList();
            });

            return (categories);
        }

        public IList<Item> ParseItems()
        {
            var items = new List<Item>();
            var categories = new List<Category>();

            var xmlDoc = XDocument.Load(_itemsXmlFileName);

            var xsd = _categoryXmlFileName.Replace(".xml", ".xsd").Replace("App_Data", "App_Data\\schemas");
            if (File.Exists(xsd))
            {
                var schemaSet = new XmlSchemaSet();
                schemaSet.Add("", xsd);
                schemaSet.Compile();
                xmlDoc.Validate(schemaSet, (o, ex) => { _log.Error(ex.Message); });
            }

            var xeItems = xmlDoc.Descendants("item").ToList();
            var xeCats  = xmlDoc.Descendants("category").ToList();

            xeItems.ForEach(xe => items.Add(
                                            new Item
                                            {
                                                Id              = xe.Element("id").Value,
                                                Title           = xe.Element("title").Value,
                                                Desc            = xe.Element("description").Value.Trim(),
                                                Price           = Convert.ToDecimal(xe.Element("price").Value),
                                                IsInStock       = Convert.ToBoolean(xe.Element("inStock").Value),
                                                IsVisible       = Convert.ToBoolean(xe.Element("visible").Value),
                                                ImageUrl        = xe.Element("imageUrl").Value,
                                                ImageAltText    = xe.Element("imageAltText").Value
                                            }));

            xeCats.ForEach(xe => categories.Add(
                                            new Category
                                            {
                                                Id              = xe.Element("id").Value,
                                                Title           = xe.Element("title").Value,
                                                Desc            = xe.Element("description").Value.Trim(),
                                                ImageUrl        = xe.Element("imageUrl").Value,
                                                ImageAltText    = xe.Element("imageAltText").Value,
                                                IsVisible       = Convert.ToBoolean(xe.Element("visible").Value),
                                                ParentId        = HasValue(xe.Element("parentCategoryId")),
                                            }));
            categories.ForEach(category =>
            {
                category.ChildCategories = categories.Where(subCategory => subCategory.ParentId == category.Id)
                                                     .Select(subCategory => subCategory)
                                                     .ToList();

                category.Parent = categories.Where(parentCategory => parentCategory.Id == category.ParentId)
                                            .Select(parentCategory =>
                                                       new Category
                                                       {
                                                           Id           = parentCategory.Id,
                                                           Title        = parentCategory.Title,
                                                           Desc         = parentCategory.Desc,
                                                           IsVisible    = parentCategory.IsVisible,
                                                           ImageUrl     = parentCategory.ImageUrl,
                                                           ImageAltText = parentCategory.ImageAltText,
                                                       })
                                            .FirstOrDefault();
            });

            categories.ForEach(c =>
            {
                var xc = xeCats.Where(xe => xe.Element("id").Value == c.Id)
                               .Select(xe => xe)
                               .Descendants("childItemId")
                               .Select(xe => xe.Value)
                               .ToList();

                c.Items = items.Where(i => xc.Contains(i.Id)).Select(i => i).ToList();
            });

            return (items);
        }

        public IList<Person> ParsePeople()
        {
            var people = new List<Person>();
            var xmlDoc = XDocument.Load(_peopleXmlFileName);

            var xsd = _peopleXmlFileName.Replace(".xml", ".xsd").Replace("App_Data", "App_Data\\schemas");
            if (File.Exists(xsd))
            {
                var schemaSet = new XmlSchemaSet();
                schemaSet.Add("", xsd);
                schemaSet.Compile();
                xmlDoc.Validate(schemaSet, (o, ex) => { _log.Error(ex.Message); });
            }

            var xePeople = xmlDoc.Descendants("person").ToList();

            xePeople?.ForEach(xe =>
            {
                people.Add(
                            new Person
                            {
                                Id              = xe.Element("id").Value,
                                IsVisible       = Convert.ToBoolean(xe.Element("visible").Value),
                                FirstName       = xe.Element("firstName").Value,
                                MiddleName      = xe.Element("middleName").Value,
                                LastName        = xe.Element("lastName").Value,
                                Title           = xe.Element("title").Value,
                                Desc            = xe.Element("description").Value.Trim(),
                                Email           = xe.Element("email").Value,
                                Phone           = xe.Element("phone").Value,
                                Fax             = xe.Element("fax").Value,
                                Address         = xe.Element("streetAddress").Value,
                                City            = xe.Element("city").Value,
                                State           = xe.Element("state").Value,
                                ZipCode         = xe.Element("postalCode").Value,
                                Country         = xe.Element("country").Value,
                                ImageUrl        = xe.Element("imageUrl").Value,
                                ImageAltText    = xe.Element("imageAltText").Value,
                            });
            });

            return (people);
        }

        public IList<News> ParseNews()
        {
            var newsItems = new List<News>();
            var xmlDoc   = XDocument.Load(_newsXmlFileName);

            var xsd = _newsXmlFileName.Replace(".xml", ".xsd").Replace("App_Data", "App_Data\\schemas");
            if (File.Exists(xsd))
            {
                var schemaSet = new XmlSchemaSet();
                schemaSet.Add("", xsd);
                schemaSet.Compile();
                xmlDoc.Validate(schemaSet, (o, ex) => { _log.Error(ex.Message); });
            }

            var xeNews = xmlDoc.Descendants("newsItem").ToList();

            xeNews?.ForEach(xe => newsItems.Add(
                                                new News
                                                {
                                                    Id           = xe.Element("id").Value,
                                                    Title        = xe.Element("title").Value,
                                                    Date         = Convert.ToDateTime(xe.Element("date").Value),
                                                    Content      = xe.Element("content").Value,
                                                    IsVisible    = Convert.ToBoolean(xe.Element("visible").Value),
                                                    ImageUrl     = xe.Element("imageUrl").Value,
                                                    ImageAltText = xe.Element("imageAltText").Value,
                                                }));
            return (newsItems);
        }

        public IList<Testimonial> ParseTestimonials()
        {
            var testimonials = new List<Testimonial>();
            var xmlDoc       = XDocument.Load(_testimonialXmlFileName);

            var xsd = _testimonialXmlFileName.Replace(".xml", ".xsd").Replace("App_Data", "App_Data\\schemas"); // quick hack. Fix this later
            if (File.Exists(xsd))
            {
                var schemaSet = new XmlSchemaSet();
                schemaSet.Add("", xsd);
                schemaSet.Compile();
                xmlDoc.Validate(schemaSet, (o, ex) => { _log.Error(ex.Message); });
            }

            var xeTests = xmlDoc.Descendants("testimonial").ToList();

            xeTests?.ForEach(xe =>
            {
                testimonials.Add(
                                    new Testimonial
                                    {
                                        Id               = xe.Element("id").Value,
                                        Title            = xe.Element("title").Value,
                                        Date             = Convert.ToDateTime(xe.Element("date").Value),
                                        Content          = xe.Element("content").Value.Trim(),
                                        ImageUrl         = xe.Element("imageUrl").Value,
                                        ImageAltText     = xe.Element("imageAltText").Value,
                                        IsVisible        = Convert.ToBoolean(xe.Element("visible").Value),
                                        Testifier        = xe.Element("testifier").Value,
                                        TestifierTitle   = xe.Element("testifierTitle").Value,
                                        TestifierCompany = xe.Element("testifierCompany").Value
                                    });
            });

            return (testimonials);
        }

        public IList<Category> LoadCatalogGraph()
        {
            var categories = new List<Category>();

            var xmlDoc   = XDocument.Load(_categoryXmlFileName);

            var xsd = _categoryXmlFileName.Replace(".xml", ".xsd").Replace("App_Data", "App_Data\\schemas"); // quick hack. Fix this later
            if (File.Exists(xsd))
            {
                var schemaSet = new XmlSchemaSet();
                schemaSet.Add("", xsd);
                schemaSet.Compile();
                xmlDoc.Validate(schemaSet, (o, ex) => { _log.Error(ex.Message); });
            }

            var xeCats = xmlDoc.Descendants("category").ToList();

            xeCats.ForEach(xe => categories.Add(
                                                new Category
                                                {
                                                    Id           = xe.Element("id").Value,
                                                    ParentId     = HasValue(xe.Element("parentCategoryId")),
                                                    Title        = xe.Element("title").Value,
                                                    Desc         = xe.Element("description").Value,
                                                    ImageUrl     = xe.Element("imageUrl").Value,
                                                    ImageAltText = xe.Element("imageAltText").Value,
                                                    IsVisible    = Convert.ToBoolean(xe.Element("visible").Value),
                                                }));

            var items = new List<Item>();
            xsd = _categoryXmlFileName.Replace(".xml", ".xsd").Replace("App_Data", "App_Data\\schemas");
            if (File.Exists(xsd))
            {
                var schemaSet = new XmlSchemaSet();
                schemaSet.Add("", xsd);
                schemaSet.Compile();
                xmlDoc.Validate(schemaSet, (o, ex) => { _log.Error(ex.Message); });
            }

            var xeItems = xmlDoc.Descendants("item").ToList();

            xeItems.ForEach(xe => items.Add(
                                            new Item
                                            {
                                                Id           = xe.Element("id").Value,
                                                Title        = xe.Element("title").Value,
                                                Desc         = xe.Element("description").Value.Trim(),
                                                Price        = Convert.ToDecimal(xe.Element("price").Value),
                                                IsInStock    = Convert.ToBoolean(xe.Element("inStock").Value),
                                                IsVisible    = Convert.ToBoolean(xe.Element("visible").Value),
                                                ImageUrl     = xe.Element("imageUrl").Value,
                                                ImageAltText = xe.Element("imageAltText").Value
                                            }));

            categories.ForEach(c =>
            {
                var xc = xeCats.Where(xe => xe.Element("id").Value == c.Id)
                               .Select(xe => xe)
                               .Descendants("childItemId")
                               .Select(xe => xe.Value)
                               .ToList();

                c.Items = items.Where(i => xc.Contains(i.Id)).Select(i => i).ToList();
            });

            return (categories);
        }

        #endregion
    }
}
